package edu.ksu.cis.indus.toolkits.bandera;

import edu.ksu.cis.bandera.tool.Tool;
import edu.ksu.cis.bandera.tool.ToolConfigurationView;
import edu.ksu.cis.bandera.tool.ToolIconView;
import edu.ksu.cis.bandera.util.BaseObservable;
import edu.ksu.cis.indus.common.soot.CompleteStmtGraphFactory;
import edu.ksu.cis.indus.processing.Environment;
import edu.ksu.cis.indus.slicer.SliceCriteriaFactory;
import edu.ksu.cis.indus.slicer.transformations.TagBasedDestructiveSliceResidualizer;
import edu.ksu.cis.indus.staticanalyses.tokens.BitSetTokenManager;
import edu.ksu.cis.indus.staticanalyses.tokens.soot.SootValueTypeManager;
import edu.ksu.cis.indus.tools.Phase;
import edu.ksu.cis.indus.tools.slicer.SlicerToolHelper;
import edu.ksu.cis.indus.tools.slicer.criteria.specification.SliceCriteriaParser;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.jibx.runtime.BindingDirectory;
import org.jibx.runtime.IBindingFactory;
import org.jibx.runtime.IMarshallingContext;
import org.jibx.runtime.IUnmarshallingContext;
import org.jibx.runtime.JiBXException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import soot.AbstractValueBox;
import soot.Scene;
import soot.SootField;
import soot.SootMethod;
import soot.Value;
import soot.jimple.FieldRef;
import soot.jimple.Stmt;
import soot.jimple.internal.JAssignStmt;
import soot.jimple.internal.JEqExpr;
import soot.jimple.internal.JIfStmt;
import soot.jimple.internal.JInvokeStmt;
import soot.jimple.internal.VariableBox;
import soot.tagkit.AbstractHost;

public final class SlicerTool extends BaseObservable implements Tool {
	public static final Object CRITERIA = "slicingCriteria";

	public static final Object CRITERIA_SPECIFICATION = "slicingCriteriaSpecification";

	public static final Object ID_OF_CONFIGURATION_TO_USE = "idOfConfigurationToUse";

	public static final Object ROOT_METHODS = "entryPoints";

	public static final Object SCENE = "scene";
	private static final List<Object> IN_ARGUMENTS_IDS;
	private static final Logger LOGGER = LoggerFactory
			.getLogger(SlicerTool.class);
	private static final List<Object> OUT_ARGUMENTS_IDS;
	private static final String TAG_NAME = "Slicer:Bandera";
	private boolean abortFlag;
	private SlicerConfiguration configuration;
	private SlicerConfigurationView configurationView;
	private boolean configurationWasProvided;
	private Scene scene;
	private final edu.ksu.cis.indus.tools.slicer.SlicerTool tool;

	static {
		IN_ARGUMENTS_IDS = new ArrayList();
		IN_ARGUMENTS_IDS.add(SCENE);
		IN_ARGUMENTS_IDS.add(ROOT_METHODS);
		IN_ARGUMENTS_IDS.add(CRITERIA);
		IN_ARGUMENTS_IDS.add(CRITERIA_SPECIFICATION);
		OUT_ARGUMENTS_IDS = new ArrayList();
		OUT_ARGUMENTS_IDS.add(SCENE);
	}

	public SlicerTool() {
		this.tool = new edu.ksu.cis.indus.tools.slicer.SlicerTool(
				new BitSetTokenManager(new SootValueTypeManager()),
				new CompleteStmtGraphFactory());
		this.tool.setTagName("Slicer:Bandera");
		this.configurationView = new SlicerConfigurationView(
				this.tool.getConfigurator());
	}

	public String getConfiguration() {
		try {
			this.configuration.slicerConfigurationStr = this.tool
					.stringizeConfiguration();

			IBindingFactory _bfact = BindingDirectory
					.getFactory(SlicerConfiguration.class);
			IMarshallingContext _mctx = _bfact.createMarshallingContext();
			_mctx.setIndent(4);

			ByteArrayOutputStream _b = new ByteArrayOutputStream();
			_mctx.marshalDocument(this.configuration, "UTF-8", null, _b);
			return _b.toString();
		} catch (JiBXException _e) {
			UnknownError _r = new UnknownError();
			_r.initCause(_e);
			throw _r;
		}
	}

	public List getInputParameterList() {
		return Collections.unmodifiableList(IN_ARGUMENTS_IDS);
	}

	public Map getOutputMap() {
		Map _outputMap = new HashMap();
		_outputMap.put(SCENE, this.scene);
		return _outputMap;
	}

	public List getOutputParameterList() {
		return Collections.unmodifiableList(OUT_ARGUMENTS_IDS);
	}

	public ToolConfigurationView getToolConfigurationView() {
		return this.configurationView;
	}

	public ToolIconView getToolIconView() {
		return this.configurationView;
	}

	public void quit() throws Exception {
		this.tool.abort();
		this.abortFlag = true;
	}

	public void run() throws Exception {
		if (LOGGER.isInfoEnabled()) {
			LOGGER.info("BEGIN: bandera slicer tool");
		}
		this.abortFlag = false;

		if (!(this.configurationWasProvided)) {
			String _msg = "No configuration was provided.  Aborting!!!";
			LOGGER.error("No configuration was provided.  Aborting!!!");
			throw new IllegalArgumentException(
					"No configuration was provided.  Aborting!!!");
		}

		this.tool.run(Phase.STARTING_PHASE, null, true);

		SlicerToolHelper.optimizeForSpaceBeforeResidualization(this.tool,
				this.configuration.retentionList);

		if (!(this.abortFlag)) {
			TagBasedDestructiveSliceResidualizer _residualizer = new TagBasedDestructiveSliceResidualizer();
			_residualizer.setTagToResidualize("Slicer:Bandera");
			_residualizer.setBasicBlockGraphMgr(this.tool
					.getBasicBlockGraphManager());
			_residualizer.residualizeSystem(this.tool.getSystem());
		}

		if (this.configuration.eraseUnnecessaryClasses) {
			SlicerToolHelper.optimizeForSpaceAfterResidualization(this.tool,
					this.configuration.retentionList);
		}

		if (LOGGER.isInfoEnabled())
			LOGGER.info("END: bandera slicer tool");
	}

	public void setConfiguration(String configStr) throws Exception {
		this.configurationWasProvided = ((configStr != null) && (configStr
				.length() > 0));

		IBindingFactory _bfact = BindingDirectory
				.getFactory(SlicerConfiguration.class);
		IUnmarshallingContext _uctx = _bfact.createUnmarshallingContext();
		this.configuration = ((SlicerConfiguration) _uctx.unmarshalDocument(
				new BufferedInputStream(new ByteArrayInputStream(configStr
						.getBytes())), null));
		this.tool
				.destringizeConfiguration(this.configuration.slicerConfigurationStr);
	}

	@SuppressWarnings("unchecked")
	public void setInputMap(Map inputArgs) {
		this.scene = ((Scene) inputArgs.get(SCENE));

		if (this.scene == null) {
			String _msg = "A scene must be provided for slicing.";
			LOGGER.error("A scene must be provided for slicing.");
			throw new IllegalArgumentException(
					"A scene must be provided for slicing.");
		}
		this.tool.setSystem(new Environment(this.scene));

		this.tool.clearCriteria();

		Collection _criteria = (Collection) (Collection) inputArgs
				.get(CRITERIA);

		if ((_criteria == null) || (_criteria.isEmpty())) {
			LOGGER.warn("Deadlock criteria will be used.");
		} else {
			this.tool.addCriteria(_criteria);

			for (Iterator _i = _criteria.iterator(); _i.hasNext();) {
				Object _o = _i.next();

				if (!(SliceCriteriaFactory.isSlicingCriterion(_o))) {
					LOGGER.error(_o
							+ " is an invalid slicing criterion.  "
							+ "All slicing criterion should be created via SliceCriteriaFactory.");
					throw new IllegalArgumentException("Slicing criteion " + _o
							+ " was not created via SliceCriteriaFactory.");
				}
			}
		}

		String _criteriaSpec = (String) inputArgs.get(CRITERIA_SPECIFICATION);

		if (_criteriaSpec == null)
			LOGGER.info("No criteria specification provided.");
		else {
			try {
				this.tool.addCriteria(SliceCriteriaParser.deserialize(
						_criteriaSpec, this.scene));
			} catch (JiBXException _e) {
				String _msg = "Error occurred while deserializing the provided criteria specification.";
				LOGGER.error("Error occurred while deserializing the provided criteria specification.");

				IllegalArgumentException _t = new IllegalArgumentException(
						"Error occurred while deserializing the provided criteria specification.");
				_t.initCause(_e);
				throw _t;
			}
		}

		Collection _rootMethods = (Collection) (Collection) inputArgs
				.get(ROOT_METHODS);

		if ((_rootMethods == null) || (_rootMethods.isEmpty())) {
			String _msg = "Atleast one method should be specified as the entry-point into the system.";
			LOGGER.error("Atleast one method should be specified as the entry-point into the system.");
			throw new IllegalArgumentException(
					"Atleast one method should be specified as the entry-point into the system.");
		}
		this.tool.setRootMethods(_rootMethods);

		String _activeConfID = (String) inputArgs
				.get(ID_OF_CONFIGURATION_TO_USE);

		if (_activeConfID == null)
			LOGGER.info("No active configuration was specified.  Using the default in the provided configuration.");
		else {
			this.tool.setActiveConfiguration(_activeConfID);
		}

		// TODO WXQ replace
		/*
		 * for (Iterator _i = _rootMethods.iterator(); _i.hasNext(); )
		 * this.tool.
		 * addCriteria(SliceCriteriaFactory.getFactory().getCriteria((SootMethod
		 * )_i.next()));
		 */
		{
			// this.tool.clearCriteria();
			for (Iterator _i = _rootMethods.iterator(); _i.hasNext();) {
				SootMethod sm = (SootMethod) _i.next();
				// add root cause to criteria
				// this.tool.addCriteria(SliceCriteriaFactory.getFactory()
				// .getCriteria(sm));
				// add shared vairables to critera.
				Iterator fi = sm.getDeclaringClass().getFields().iterator();
				while (fi.hasNext()) {
					SootField fd = (SootField) fi.next();
					if (fd.isPublic()) {
						Iterator mit = sm.getDeclaringClass().getMethods()
								.iterator();
						while (mit.hasNext()) {
							SootMethod m = (SootMethod) mit.next();
							Iterator it = m.getActiveBody().getUnits()
									.iterator();
							while (it.hasNext()) {
								Object item = it.next();
								if (item instanceof Stmt)
									if (isRef((Stmt) item, fd)) {
										this.tool
												.addCriteria(SliceCriteriaFactory
														.getFactory()
														.getCriteria(m,
																(Stmt) item,
																true));
									}
							}
						}

					}
				}
			}
		}
	}

	private boolean isRef(Stmt item, SootField fd) {
		if (item instanceof JAssignStmt) {
			JAssignStmt jas = (JAssignStmt) item;
			if (isRef(jas.getLeftOp(),fd)) {
				return true;
			}else{
				return isRef(jas.getRightOp(),fd);
			}
		}
		
		if(item instanceof JIfStmt){
			JIfStmt jif = (JIfStmt)item;
			return isRef(jif.getCondition(),fd);
		}
		
		if(item instanceof JInvokeStmt){
			JInvokeStmt jiv = (JInvokeStmt)item;
			List args = jiv.getInvokeExpr().getArgs();
			for(int i = 0; i < args.size(); i++){
				if(isRef(((Value)args.get(i)), fd))	{
					return true;
				}
			}
		}
			
		return false;
	}

	private boolean isRef(Value rightOp, SootField fd) {
		if(rightOp instanceof FieldRef){
			FieldRef sf = (FieldRef)rightOp;
			return sf.getField().equals(fd);
		}
		if(rightOp instanceof JEqExpr){
			JEqExpr jeq = (JEqExpr)rightOp;
			if(isRef(jeq.getOp1(),fd)){
				return true;
			}else{
				return isRef(jeq.getOp2(),fd);
			}
		}
		return false;
	}
}

/*
 * Location: D:\tool\banderaCLI-1.0a4\lib\indus\slicer.jar Qualified Name:
 * edu.ksu.cis.indus.toolkits.bandera.SlicerTool Java Class Version: 5 (49.0)
 * JD-Core Version: 0.5.3
 */